#include "media_stream_manager.h"

#include <algorithm>
#include <inttypes.h>
#include <string.h>
#include <sys/prctl.h>
#include <sys/time.h>
#include <vector>
#include <cassert>
#include <memory>
#include <utility>
#include "camera_video_enc.h"
#include "audio_enc.h"
#include "base_log.h"

namespace El {
namespace Media {

StreamSourceManager &StreamSourceManager::GetInstance(int32_t ch, int32_t type)
{
    static std::map<std::pair<int32_t, int32_t>, StreamSourceManager *> instances;
    static std::mutex mutex;

    std::lock_guard<std::mutex> lock(mutex);
    auto key = std::make_pair(ch, type);
    auto it = instances.find(key);
    if (it == instances.end()) {
        StreamSourceManager *instance = new StreamSourceManager(ch, type);
        instances.emplace(key, instance);
        return *instance;
    }
    return *it->second;
}

StreamSourceManager::StreamSourceManager(int32_t ch, int32_t type) : ch_(ch), type_(type) {}

StreamSourceManager::~StreamSourceManager() {}

int32_t StreamSourceManager::Start(const Proc &proc)
{
    if (!start_.exchange(true)) {
        LOG_WARN("StreamSourceManager is starting ch:{}, type:{}", ch_, type_);

        halHandle_ = Hal::ICameraVideoEnc::GetInstance(ch_, type_).Start([this](const VideoEncPacket &packet) {
            auto media = std::make_shared<MediaFrame>();
            media->SetUsePool(true);
            if (!media->PutBuffer(packet.buf, packet.len)) {
                LOG_ERROR("Failed to put buffer");
                return;
            }
            media->SetPts(packet.pts);
            media->SetUtc(packet.utc);
            media->SetFrameType(packet.frameType);
            media->SetCodecType(packet.codecType);
            OnStream(media);
        });

        halAudioHandle_ = Hal::IAudioEnc::GetInstance(ch_, type_).Start([this](const AudioEncPacket &packet) {
            auto media = std::make_shared<MediaFrame>();
            media->SetUsePool(true);
            if (!media->PutBuffer(packet.buf, packet.len)) {
                LOG_ERROR("Failed to put buffer");
                return;
            }
            media->SetPts(packet.pts);
            media->SetUtc(packet.utc);
            media->SetFrameType(packet.frameType);
            media->SetCodecType(packet.codecType);
            OnStream(media);
        });
    }

    return callbackManager_.Register(proc);
}

void StreamSourceManager::Stop(int32_t handle)
{
    callbackManager_.Unregister(handle);
}

void StreamSourceManager::OnStream(MediaFramePtr &media)
{
    callbackManager_.InvokeAll(media);
}

} // namespace Media
} // namespace El